Functor 對初學者來說實在有點難懂,所以不知不覺已來到第四篇,希望看完這一系列可以真的懂他在幹嘛。 基本上除了系列 1 之外,其他都是在探究 Box 這種 data type
(↑ 不知道已出現幾次的 Box data type 圖)
神奇的運算 map 能接受 Functor 當作輸入參數,回傳另一個 Box。
雖然前面其實已經說過這是什麼,但需要再強調複習一次
const Box = f => ({
map: g => Box( x => g(f(x)) ),
}
un-nest 扁平化,接受 Functor 當作參數,回傳把最外層 Context 拿掉後裡面的東西
像上圖,很常時候我們為了避免 Side Effect 例如 Math.random()
、 console.log
,會先把他們丟到一個 Box 裡,但這樣做完運算會回傳 nesting 的 Box (也就是 Box 會包在另一個 Box,可以看下圖比較好懂),這樣導致後面運算變複雜,所以才會有這個方法,讓他 un-nest 扁平化,變成只要一層 Box 就好
同義詞還有
Join
跟bind
(Haskell) 的說法但我個人比較喜歡flatMap
較符合方法實際在做的事。
const Box = f => ({
flatMap: x => f(x)
}
若我們把前一篇提過的 runEffect 也放進來。
const Box = f => ({
flatMap: x => f(x),
runEffect: x=> f(x)
}
恩?你可以會有疑惑說 runEffect
跟 flatMap
不是一模一樣嗎?
沒錯,當然你可以只用一個就好,但 runEffect
顧名思義我們都拿來執行 Effect 檢視內容。但若我們只是想要扁平化,除掉 nest 而已,並沒有想導致 Side Effect 但卻用 runEffect
這個名稱似乎有點怪,所以還是建議分開寫成兩個方法
map + flatMap
程式寫到最後會發現不斷再 .map().flatMap()
那不如直接寫一個快速方法是包含這兩個方法的
const Box = f => ({
map: g => Effect( x => g(f(x)) ),
flatMap: x => f(x),
chain: g => Effect(f).map(g).flatMap()
}
下一篇會實際用程式解說以上方法
如有錯誤或需要改進的地方,拜託跟我說。
我會以最快速度修改,感謝您
歡迎追蹤我的部落格,除了技術文也會分享一些在矽谷工作的甘苦。
const Box = f => ({ map: g => Effect( x => g(f(x)) ), flatMap: x => f(x), chain: g => Effect(f).map(g).flatMap() }
嗨~ 這邊的範例看起來有點怪怪的
不知改成這樣對不對:
const Box = f => ({
map: g => Box( x => g(f(x)) ),
flatMap: x => f(x),
chain: g => Box(f).map(g.flatMap)
}
第二點看了下一篇例子大概知道問題在哪。
原本還以為 chain 是直接把兩個 Box 連在一起
const a = Box((x) => x + 1)
const b = Box((x) => x * 10)
const c = a.chain(b)
結果是如下,則原本的 g => Box(f).map(g).flatMap()
就對了。
const a = (x) => x + 1
const b = (x) => Box.of(x * 10)
const c = Box.of(4).map(a).chain(b)